package icmp

import (
	"encoding/binary"
	"fmt"
	"network/ip"
	"reflect"
)

// Echo ping包
type Echo struct {
	ICMPv4
	buf []byte
}

const (
	EchoID   = 4
	EchoSeq  = 6
	EchoData = 8
)

func NewEcho(buf []byte) (*Echo, error) {
	if !ip.VerifyCheckSum(buf) {
		return nil, ip.ErrEchoCheckSum
	}
	return &Echo{
		ICMPv4: buf,
		buf:    buf,
	}, nil
}

func (e Echo) Identifier() uint16 {
	return binary.BigEndian.Uint16(e.buf[EchoID:])
}

func (e Echo) Seq() uint16 {
	return binary.BigEndian.Uint16(e.buf[EchoSeq:])
}

func (e Echo) Data() []byte {
	data := make([]byte, len(e.buf)-EchoData)
	copy(data, e.buf[EchoData:])
	return data
}

func (e Echo) Pong() []byte {
	buf := make([]byte, len(e.buf))
	copy(buf, e.buf)
	buf[Type] = PongType
	binary.BigEndian.PutUint16(buf[Checksum:], 0)
	binary.BigEndian.PutUint16(buf[Checksum:], CalEchoCheckSum(buf))
	return buf
}

// String 使用反射将ip数据报里面的所有字段读取出来
func (e Echo) String() string {
	methods := reflect.ValueOf(e)
	methodTypes := reflect.TypeOf(e)
	var str string
	for a := 0; a < methods.NumMethod(); a++ {
		if methodTypes.Method(a).Name == "String" {
			continue
		}
		str += fmt.Sprint(
			methodTypes.Method(a).Name, ":",
			methods.Method(a).Call([]reflect.Value{})[0].Interface(), ";",
		)
	}
	if len(str) != 0 {
		return fmt.Sprintf("{%s}", str)
	}
	return str
}

func SplitEcho(buf []byte) []uint16 {
	arr := make([]uint16, 0, 32)
	var i int
	for i < len(buf) {
		if i == Checksum {
			arr = append(arr, 0)
		} else {
			arr = append(arr, binary.BigEndian.Uint16(buf[i:]))
		}
		i += 2
	}

	if i < len(buf) {
		arr = append(arr, binary.BigEndian.Uint16([]byte{buf[i], 0}))
	}
	return arr
}

func CalEchoCheckSum(buf []byte) uint16 {
	return ip.CalCheckSum(SplitEcho(buf))
}
